#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <cstring>
#include <iostream>
#include <fstream>
#include "binifile.h"
#include "blog.h"
#include "bstrings.h"

namespace butils {
namespace ini
{

// 构造函数，会初始化注释字符集合flags_（容器），目前只使用#和;作为注释前缀
file::file()
	:comment_delimiter_("#")
{
}

file::~file()
{
	release();
}

//解析一行数据，得到键值
/* --------------------------------------------------------------------------*/
/**
 * @brief   parse
 *
 * @param   content 数据源指针
 * @param   key     键名
 * @param   value   键值
 * @param   c       分隔符
 *
 * @return  bool
 */
/* ----------------------------------------------------------------------------*/
bool file::parse(const std::string &content, std::string *key, std::string *value)
{
    return strings::split(content, "=", key, value);
}

int file::update_section(const std::string &cline, const std::string &comment, const std::string &commentr, section **sec)
{
    // 查找右中括号
    size_t index = cline.find_first_of(']');
    if (index == std::string::npos) {
        err_msg_ = std::string("no matched ] found");
        return ERR_UNMATCHED_BRACKETS;
    }

    int len = static_cast<int>(index) - 1;
    // 若段名为空，继续下一行
    if (len <= 0) {
        err_msg_ = std::string("section name is empty");
        return ERR_SECTION_EMPTY;
    }

    // 取段名
	std::string s(cline, 1, len);
    strings::trim(s);
    //检查段是否已存在
    if (get_section(s) != NULL) {
		*sec = get_section(s);
		return 0;
        //err_msg_ = std::string("section ") + s + std::string("already exist");
        //return ERR_SECTION_ALREADY_EXISTS;
    }

    // 申请一个新段，由于map容器会自动排序，打乱原有顺序，因此改用vector存储（sections_vt_）
	section *new_sec = new section();
    // 填充段名
	new_sec->name = s;
    // 填充段开头的注释
	new_sec->comment = comment;
	new_sec->commentr = commentr;
    sections_vt_.push_back(new_sec);
    *sec = new_sec;
    return 0;
}

int file::add_key_value_pair(const std::string &cline, const std::string &comment,
                             const std::string &commentr, section *sec)
{
    std::string key, value;
    if (!parse(cline, &key, &value)) {
        err_msg_ = std::string("parse line failed:") + cline;
        return ERR_PARSE_KEY_VALUE_FAILED;
    }

    item im;
	im.key = key;
	im.value = value;
	im.comment = comment;
	im.commentr = commentr;
    sec->items.push_back(im);
    return 0;
}

int file::load(const std::string &fname)
{
    release();
	file_path_ = fname;
    std::ifstream ifs(file_path_);
	if (!ifs) {
		logw("{} not exists!!!", fname);
		return 0;
	}
    if (!ifs.is_open()) {
        err_msg_ = std::string("open ") + file_path_ + std::string(" file failed");
        return ERR_OPEN_FILE_FAILED;
    }
    //增加默认段，即 无名段""
	section *cur_sec = new section();
	cur_sec->name = "";
    sections_vt_.push_back(cur_sec);

	int err = 0;
	std::string line;		// 带注释的行
	std::string clean_line;	// 去掉注释后的行
	std::string comment;
	std::string commentr;
    // 每次读取一行内容到line
    while (std::getline(ifs, line)) {
        strings::trim(line);
        // UTF-8 BOM头
        if (line.size() >= 3
            && (unsigned char)line[0] == 0xEF
            && (unsigned char)line[1] == 0xBB
            && (unsigned char)line[2] == 0xBF) {
            // 过滤掉BOM头数据
            line = line.substr(3);
        }
        // step 0，空行处理，如果长度为0，说明是空行，添加到comment，当作是注释的一部分
        if (line.empty()) {
            comment += delim;
            continue;
        }

        // step 1
        // 如果行首不是注释，查找行尾是否存在注释
        // 如果该行以注释开头，添加到comment，跳过当前循环，continue
        if (is_comment_line(line)) {
            comment += line + delim;
            continue;
        }

        // 如果行首不是注释，查找行尾是否存在注释，若存在，切割该行，将注释内容添加到commentr
        strings::split(line, comment_delimiter_, &clean_line, &commentr);

        // step 2，判断line内容是否为段或键
        //段开头查找 [
        if (clean_line[0] == '[') {
            err = update_section(clean_line, comment, commentr, &cur_sec);
        } else {
            // 如果该行是键值，添加到section段的items容器
			err = add_key_value_pair(clean_line, comment, commentr, cur_sec);
        }

        if (err != 0) {
            ifs.close();
            return err;
        }
        // comment清零
        comment = "";
		commentr = "";
    }

    ifs.close();
    return 0;
}

int file::save()
{
    return save_as(file_path_);
}

int file::save_as(const std::string &fname)
{
	std::string data = "";

    /* 载入section数据 */
    for (auto sect = sections_vt_.begin(); sect != sections_vt_.end(); ++sect) {
        if ((*sect)->comment != "") {
            data += (*sect)->comment;
        }

        if ((*sect)->name != "") {
            data += std::string("[") + (*sect)->name + std::string("]");
            data += delim;
        }

        if ((*sect)->commentr != "") {
            data += " " + comment_delimiter_ +(*sect)->commentr;
        }

        /* 载入item数据 */
        for (auto item = (*sect)->items.begin(); item != (*sect)->items.end(); ++item) {
            if (item->comment != "") {
                data += item->comment;
                if (data[data.length()-1] != '\n') {
                    data += delim;
                }
            }

            data += item->key + "=" + item->value;

            if (item->commentr != "") {
                data += " " + comment_delimiter_ + item->commentr;
            }

            if (data[data.length()-1] != '\n') {
                data += delim;
            }
        }
    }

    std::ofstream ofs(fname);
    ofs << data;
    ofs.close();
    return 0;
}

section *file::get_section(const std::string &sec /*=""*/)
{
    for (auto it = sections_vt_.begin(); it != sections_vt_.end(); ++it) {
        if ((*it)->name == sec) {
            return *it;
        }
    }
    return NULL;
}

int file::sections(std::vector<std::string> *sections)
{
    for (auto it = sections_vt_.begin(); it != sections_vt_.end(); ++it) {
        sections->push_back((*it)->name);
    }
    return static_cast<int>(sections->size());
}

int file::section_cnt()
{
    return static_cast<int>(sections_vt_.size());
}

int file::get_string_value(const std::string &sec, const std::string &key, std::string *value)
{
    return get_value(sec, key, value);
}

int file::get_int_value(const std::string &sec, const std::string &key, int *value)
{
	std::string val;
	int err = get_value(sec, key, &val);
	*value = atoi(val.c_str());
    return err;
}

int file::get_double_value(const std::string &sec, const std::string &key, double *value)
{
	std::string val;
	int err = get_value(sec, key, &val);
    *value = atof(val.c_str());
    return err;
}

int file::get_bool_value(const std::string &sec, const std::string &key, bool *value)
{
	std::string val;
	int err = get_value(sec, key, &val);
    if (strings::compare_ingore_case(val, "true") || strings::compare_ingore_case(val, "1")) {
        *value = true;
    } else if (strings::compare_ingore_case(val, "false") || strings::compare_ingore_case(val, "0")) {
        *value = false;
	} else {
		err = ERR_EXEC_VALUE;
	}
    return err;
}

/* 获取section段第一个键为key的string值，成功返回获取的值，否则返回默认值 */
std::string file::string_value(const std::string &sec, const std::string &key, const std::string &default_value)
{
	std::string value;
	if (0 != get_string_value(sec, key, &value))
		value = default_value;
	return value;
}

/* 获取section段第一个键为key的int值，成功返回获取的值，否则返回默认值 */
int file::int_value(const std::string &sec, const std::string &key, int default_value)
{
	int value = 0;
	if (0 != get_int_value(sec, key, &value))
		value = default_value;
	return value;
}

/* 获取section段第一个键为key的double值，成功返回获取的值，否则返回默认值 */
double file::double_value(const std::string &sec, const std::string &key, double default_value)
{
	double value = 0;
	if (0 != get_double_value(sec, key, &value))
		value = default_value;
	return value;
}

/* 获取section段第一个键为key的bool值，成功返回获取的值，否则返回默认值 */
bool file::bool_value(const std::string &sec, const std::string &key, bool default_value)
{
	bool value = false;
	if (0 != get_bool_value(sec, key, &value))
		value = default_value;
	return value;
}

/* 获取注释，如果key=""则获取段注释 */
int file::string_comment(const std::string &sec, const std::string &key, std::string *comment)
{
    section *sect = get_section(sec);
    if (sect == NULL) {
        err_msg_ = std::string("not find the section ") + sec;
        return ERR_NOT_FOUND_SECTION;
    }
    if (key == "") {
        *comment = sect->comment;
        return RET_OK;
    }
    for (auto it = sect->begin(); it != sect->end(); ++it) {
        if (it->key == key) {
            *comment = it->comment;
            return RET_OK;
        }
    }
    err_msg_ = std::string("not find the key ") + key;
    return ERR_NOT_FOUND_KEY;
}

/* 获取行尾注释，如果key=""则获取段的行尾注释 */
int file::string_commentr(const std::string &sec, const std::string &key, std::string *commentr)
{
    section *sect = get_section(sec);
    if (sect == NULL) {
        err_msg_ = std::string("not find the section ") + sec;
        return ERR_NOT_FOUND_SECTION;
    }

    if (key == "") {
        *commentr = sect->commentr;
        return RET_OK;
    }

    for (auto it = sect->begin(); it != sect->end(); ++it) {
        if (it->key == key) {
            *commentr = it->commentr;
            return RET_OK;
        }
    }
    err_msg_ = std::string("not find the key ") + key;
    return ERR_NOT_FOUND_KEY;
}

int file::get_value(const std::string &sec, const std::string &key, std::string *value)
{
	std::string comment;
    return get_value(sec, key, value, &comment);
}

int file::get_value(const std::string &sec, const std::string &key, std::string *value, std::string *comment)
{
    section *sect = get_section(sec);
    if (sect == NULL) {
        err_msg_ = std::string("not find the section ") + sec;
        return ERR_NOT_FOUND_SECTION;
    }
    for (auto it = sect->begin(); it != sect->end(); ++it) {
        if (it->key == key) {
            *value = it->value;
            *comment = it->comment;
            return RET_OK;
        }
    }
    err_msg_ = std::string("not find the key ") + key;
    return ERR_NOT_FOUND_KEY;
}

int file::array_values(const std::string &sec, const std::string &key, std::vector<std::string> *values)
{
	std::vector<std::string> comments;
    return array_values(sec, key, values, &comments);
}

int file::array_values(const std::string &sec, const std::string &key, std::vector<std::string> *values, std::vector<std::string> *comments)
{
    section *sect = get_section(sec);
    if (sect == NULL) {
        err_msg_ = std::string("not find the section ")+ sec;
        return ERR_NOT_FOUND_SECTION;
    }
	values->clear();
	comments->clear();
    for (auto it = sect->begin(); it != sect->end(); ++it) {
        if (it->key == key) {
            values->push_back(it->value);
            comments->push_back(it->comment);
        }
    }
    if (values->size() == 0) {
        err_msg_ = std::string("not find the key ") + key;
        return ERR_NOT_FOUND_KEY;
    }
    return RET_OK;
}

bool file::has_section(const std::string &sec)
{
    return (get_section(sec) != NULL);
}

bool file::has_key(const std::string &sec, const std::string &key)
{
    section *sect = get_section(sec);
    if (sect != NULL) {
        for (auto it = sect->begin(); it != sect->end(); ++it) {
            if (it->key == key) {
                return true;
            }
        }
    }
    return false;
}

int file::set_value(const std::string &sec, const std::string &key, const std::string &value, const std::string &comment/*=""*/)
{
    section *sect = get_section(sec);
	std::string comt = comment;
    if (comt != "") {
        comt = comment_delimiter_ + comt;
    }
    if (sect == NULL) {
        //如果段不存在，新建一个
        sect = new section();
        if (sect == NULL) {
            err_msg_ = std::string("no enough memory!");
            return ERR_NO_ENOUGH_MEMORY;
        }
        sect->name = sec;
        if (sect->name == "") {
            // 确保空section在第一个
            sections_vt_.insert(sections_vt_.begin(), sect);
        } else {
            sections_vt_.push_back(sect);
        }
    }
    for (auto it = sect->begin(); it != sect->end(); ++it) {
        if (it->key == key) {
            it->value = value;
            it->comment = comt;
            return RET_OK;
        }
    }
    // not found key
    item im;
    im.key = key;
    im.value = value;
    im.comment = comt;
    sect->items.push_back(im);
    return RET_OK;
}

int file::set_string_value(const std::string &sec, const std::string &key, const std::string &value)
{
    return set_value(sec, key, value);
}

int file::set_int_value(const std::string &sec, const std::string &key, int value)
{
    return set_value(sec, key, std::to_string(value));
}

int file::set_double_value(const std::string &sec, const std::string &key, double value)
{
    return set_value(sec, key, std::to_string(value));
}

int file::set_bool_value(const std::string &sec, const std::string &key, bool value)
{
    if (value) {
        return set_value(sec, key, "true");
    } else {
        return set_value(sec, key, "false");
    }
}

int file::set_comment(const std::string &sec, const std::string &key, const std::string &comment)
{
    section *sect = get_section(sec);
    if (sect == NULL) {
        err_msg_ = std::string("Not find the section ")+sec;
        return ERR_NOT_FOUND_SECTION;
    }
    if (key == "") {
        sect->comment = comment;
        return RET_OK;
    }
    for (auto it = sect->begin(); it != sect->end(); ++it) {
        if (it->key == key) {
            it->comment = comment;
            return RET_OK;
        }
    }
    err_msg_ = std::string("not find the key ")+key;
    return ERR_NOT_FOUND_KEY;
}

int file::set_commentr(const std::string &sec, const std::string &key, const std::string &commentr)
{
    section *sect = get_section(sec);
    if (sect == NULL) {
        err_msg_ = std::string("Not find the section ")+sec;
        return ERR_NOT_FOUND_SECTION;
    }
    if (key == "") {
        sect->commentr = commentr;
        return RET_OK;
    }
    for (auto it = sect->begin(); it != sect->end(); ++it) {
        if (it->key == key) {
            it->commentr = commentr;
            return RET_OK;
        }
    }
    err_msg_ = std::string("not find the key ")+key;
    return ERR_NOT_FOUND_KEY;
}

void file::comment_delimiter(const std::string &delimiter)
{
	comment_delimiter_ = delimiter;
}

void file::delete_section(const std::string &sec)
{
    for (auto it = sections_vt_.begin(); it != sections_vt_.end(); ) {
        if ((*it)->name == sec) {
            delete *it;
            it = sections_vt_.erase(it);
            break;
        } else {
            it++;
        }
    }
}

void file::delete_key(const std::string &sec, const std::string &key)
{
    section *sect = get_section(sec);
    if (sect != NULL) {
        for (auto it = sect->begin(); it != sect->end();) {
            if (it->key == key) {
                it = sect->items.erase(it);
                break;
            } else {
                it++;
            }
        }
    }
}

/*-------------------------------------------------------------------------*/
/**
  @brief    release: 释放全部资源，清空容器
  @param    none
  @return   none
 */
/*--------------------------------------------------------------------------*/
void file::release()
{
	file_path_.clear();

    for (auto it = sections_vt_.begin(); it != sections_vt_.end(); ++it) {
        delete (*it);  // 清除section
    }
    sections_vt_.clear();
}

/*-------------------------------------------------------------------------*/
/**
  @brief    判断是否是注释
  @param    str 一个string变量
  @return   如果是注释则为真
 */
/*--------------------------------------------------------------------------*/
bool file::is_comment_line(const std::string &str)
{
    return strings::start_with(str, comment_delimiter_);
}

/*-------------------------------------------------------------------------*/
/**
  @brief    print for debug
  @param    none
  @return   none
 */
/*--------------------------------------------------------------------------*/
void file::print()
{
    logm("文件路径: {}", file_path_);
    logm("注释符: {}", comment_delimiter_);

    for (auto it = sections_vt_.begin(); it != sections_vt_.end(); ++it) {
        const section* sec = *it;
        logm("[{}] {}", sec->name, strings::strip(sec->comment));
        if ((*it)->commentr != "") {
            logm("{}", sec->commentr);
        }

        for (auto i = sec->items.begin(); i != sec->items.end(); ++i) {
            logm("  {}={} {}", i->key, i->value, strings::strip(i->comment));
            if (i->commentr != "") {
                logm("{}", i->commentr);
            }
        }
    }
    return;
}

const std::string& file::last_err_msg()
{
    return err_msg_;
}

}  /* namespace inifile */
} //namespace butils
